/**
  ******************************************************************************
  * @file           
  * @author         古么宁
  * @brief          IEC 104 文件传输相关部分
  * @note
  * 召唤目录 
  * <pre>
  *  master                           slave
  *    | ----TI=210,COT=5,operate=1---> | 主站召唤目录
  *    | <---TI=210,COT=5,operate=2---- | 从站召唤目录确认
  * </pre>
  * 
  * 读文件传输过程
  * <pre>
  *  master                           slave
  *    | ----TI=210,COT=6,operate=3---> | 主站读文件激活
  *    | <---TI=210,COT=7,operate=4---- | 从站读文件激活确认
  *    | <---TI=210,COT=5,operate=5---- | 从站读文件数据传输
  *    | <---         ...          ---- | 从站读文件数据传输
  *    | ----TI=210,COT=5,operate=6---> | 主站读文件数据确认
  *    | <---TI=210,COT=5,operate=5---- | 从站读文件数据传输
  *    | <---         ...          ---- | 从站读文件数据传输
  *    | ----TI=210,COT=5,operate=6---> | 主站读文件数据确认
  * </pre>
  * 
  * 写文件过程
  * <pre>
  *  master                           slave
  *    | ----TI=210,COT=6,operate=7---> | 主站写文件激活
  *    | <---TI=210,COT=7,operate=8---- | 从站写文件激活确认
  *    | ----TI=210,COT=5,operate=9---> | 主站写文件数据传输
  *    | ----         ...          ---> | 主站写文件数据传输
  *    | <---TI=210,COT=5,operate=10--- | 从站写文件激活确认
  *    | ----TI=210,COT=5,operate=9---> | 主站写文件数据传输
  *    | ----         ...          ---> | 主站写文件数据传输
  *    | <---TI=210,COT=5,operate=10--- | 从站写文件激活确认
  * <pre>
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <string.h>

#include "iec10x.h"
#include "iec10x_link.h"
#include "iec10x_telesgnl.h"
#include "iec10x_event.h"
#include "iec10x_debug.h"
#include "iec10x_fs.h"

#include "COT.h"

#if (ENABLE_FILE_TRANS)
#include "iec_file.h"
/* Private types ------------------------------------------------------------*/

/// 主机接收从机文件过程参数
struct frecv_arg {
	fs_callbackfn_t callbackfn ; 
	char  *         file       ;
	iec_file_t *    iecfile    ;
	int             fsize      ;
	int             ftell      ;
};


/// 召唤目录参数
struct dir_cmd_arg {
	dir_callbackfn_t   callbackfn ; 
	unsigned char      ASDUlen    ; ///< 数据包长度
	unsigned char      ASDU[255]  ; ///< 文件夹名称
};


struct file_cmd_arg {
	fs_callbackfn_t    callbackfn ; 
	unsigned char      ASDUlen    ; ///< 数据包长度
	unsigned char      ASDU[255]  ; ///< 数据包
};

/* Private macro ------------------------------------------------------------*/

/* 回复数据确认帧模式 */
#define ACK_PACKET ACK_SLIDING_WINDOW

/* Private variables --------------------------------------------------------*/
/* Global  variables --------------------------------------------------------*/
/* Private function prototypes ----------------------------------------------*/
/* Gorgeous Split-line ------------------------------------------------------*/


/**
  * @author   古么宁
  * @brief    关闭文件目录传输，并释放中间资源
  * @param    bus      : 总线
  * @param    result   : 传输结果
  * @return   don't care
*/
static void iec104mst_dir_close(iec10x_t * bus , int result)
{
	struct iec_slist * node = bus->filelist.next ;
	struct file_node  * file;
	
	if (bus->fs.argv[0]) {
		dir_callbackfn_t callbackfn ;
		callbackfn = (dir_callbackfn_t)bus->fs.argv[0];
		callbackfn(bus,bus->filelist.next,result);
	}

	while (node) {
		file = container_of(node,struct file_node,node);
		node = node->next ;
		iec_free(file);
	}
	
	bus->busy         &= ~IEC10x_FILE;    // 释放总线状态
	bus->filelist.next = NULL ;
	bus->interrupt     = NULL ;
	memset(&bus->fs,0,sizeof(struct iec_do));
}



/**
  * @brief    主站目录传输流程中断，关闭文件传输项中间变量
  * @param    bus      : 总线
  * @return   don't care
*/
static void iec104mst_dir_exit(iec10x_t * bus)
{
	iec104mst_dir_close(bus,-1);
}


/**
  * @author   古么宁
  * @brief    从机发送召唤目录报文
  * @param    bus      : 总线
  * @param    _arg     : struct dir_cmd_arg 召唤目录相关参数
  * @return   don't care
*/
static void iec104mst_dir_command(iec10x_t * bus, void * _arg)
{
	struct dir_cmd_arg *arg = (struct dir_cmd_arg *) _arg; 
	iec104_protocol_send(bus,arg->ASDU,arg->ASDUlen,0);
	bus->fs.argv[0] = (size_t)arg->callbackfn;
	bus->busy      |= IEC10x_FILE;
	bus->interrupt  = iec104mst_dir_exit;
}



/**
  * @author   古么宁
  * @brief    从机返回目录确认报文，解析并保存
  * @param    bus      : 总线
  * @param    ASDUrx   : 接收到的 ASDU 数据包
  * @param    ASDUlen  : 接收到的 ASDU 数据包长度
  * @return   don't care
*/
static void iec104mst_dir_confirm(iec10x_t * bus , uint8_t * ASDU,int32_t len)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct dir_confirm_pkt * pkt ;
	struct file_node       * filnode ;
	unsigned char          * info ;
	int                      namelen , size;

	if (!bus->fs.argv[0]) {
		/* 无应用处理函数，生成的列表没有用武之地，丢弃数据 */
		return ;
	}

	info = &ASDU[IEC_INFO + bus->infoaddrlen];
	pkt  = (struct dir_confirm_pkt*)info ;

	if (pkt->result) {
		/* 从机返回读取文件夹失败 */
		iec104mst_dir_close(bus,-1);
		iec10x_warn(bus,"read directory failed!\r\n");
		return ;
	}

	info = pkt->data  ;

	if (pkt->files > (len - (info - ASDU)) / sizeof(struct dir_file_list) ) {
		/* 如果文件数超出理论值，认为报错误 */
		bus->busy &= ~IEC10x_FILE;
		return ;
	}

	for (int i = 0 ; i < pkt->files ; ++i) {
		namelen = *info++;
		size    = namelen + sizeof(struct file_node) ;
		filnode = iec_malloc(size);
		if (NULL == filnode) {
			return ;
		}

		memset(filnode,0,size);

		/* 加载文件信息，此处报文结构参考 struct dir_file_list */
		filnode->namelen = namelen ;
		for (int j = 0 ; j < namelen ; ++j) {
			filnode->name[j] = *info++;
		}
		
		/* 此时 info[0] 为文件属性，此处忽略 */
		filnode->fsize = info[1]|(info[2]<<8)|(info[3]<<16)|(info[4]<<24);
		memcpy(&filnode->ftime,&info[5],sizeof(struct CP56Time2a));
		info += (1 + 4 + sizeof(struct CP56Time2a));

		/* 头插法加入链表 */
		filnode->node.next = bus->filelist.next ;
		bus->filelist.next = &filnode->node ;
	}

	if (0 == pkt->continuation) {
		/* 无后续报文，即传输完所有的文件列表，调用回调后释放内存 */
		iec104mst_dir_close(bus,0);
	}
}

/**
  * @author   古么宁
  * @brief    主站结束当前文件传输流程，并关闭文件传输项中间变量
  * @param    bus      : 总线
  * @param    result   : 传输结果
  * @return   don't care
*/
static void iec104mst_fclose(iec10x_t * bus,int result)
{
	/* 文件接收流程中间参数，存于 bus->fs.argv */
	struct frecv_arg * tmp = (struct frecv_arg *)bus->fs.argv;
	if (tmp->iecfile) {
		iec_fclose(tmp->iecfile);
	}
	
	if (tmp->callbackfn) {
		tmp->callbackfn(bus,tmp->file,result);
	}

	if (tmp->file) {
		iec_free(tmp->file) ;
	}

	bus->busy     &= ~IEC10x_FILE ;
	bus->interrupt = NULL         ;
	memset(&bus->fs,0,sizeof(struct iec_do));
}


/**
  * @brief    主站文件传输流程中断，关闭文件传输项中间变量
  * @param    bus      : 总线
  * @note     此函数交由 bus->interrupt 调用
  * @return   don't care
*/
static void iec104mst_fexit(iec10x_t * bus)
{
	iec104mst_fclose(bus,-1);
}


/**
  * @author   古么宁
  * @brief    主站发送文件激活命令，等待从机响应
  * @param    bus      : 总线
  * @param    _arg     : struct frecv_arg 激活文件相关参数
  * @return   don't care
*/
static void iec104mst_fopen(iec10x_t * bus, void * _arg)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct read_cmd_pkt * pkt ;
	struct frecv_arg * tmp ;
	struct file_cmd_arg *arg = (struct file_cmd_arg *) _arg; 

	memset(&bus->fs,0,sizeof(struct iec_do));

	pkt = (struct read_cmd_pkt*)&arg->ASDU[IEC_INFO+bus->infoaddrlen];

	/* 传输过程参数 */
	tmp = (struct frecv_arg *)bus->fs.argv;
	tmp->callbackfn = arg->callbackfn;
	tmp->file       = iec_malloc(pkt->namelen + 1);
	if (tmp->file == NULL) {
		iec10x_warn(bus,"cannot malloc for file.\r\n");
		iec104mst_fclose(bus,-1);
		return ;
	}
	else {
		memcpy(tmp->file,pkt->name,pkt->namelen);
		tmp->file[pkt->namelen] = '\0';
	}

	bus->busy     |= IEC10x_FILE;
	bus->interrupt = iec104mst_fexit;
	iec104_protocol_send(bus,arg->ASDU,arg->ASDUlen,0);
}



/**
  * @author   古么宁
  * @brief    主站发送文件数据包至从站
  * @param    bus      : 总线
  * @note     此函数交由 bus->fs.todo 调用
  * @return   don't care
*/
static void iec104mst_fwrite(iec10x_t * bus)
{
	/* 文件发送流程中间参数，存于 bus->fs.argv */
	struct frecv_arg * tmp = (struct frecv_arg *)bus->fs.argv;
	unsigned char * ASDU = ASDU_TXBUFF(bus);
	unsigned char sumchk = 0;
	int readn = 0, continuation = 0;
	int remain = 0, ftell = 0;
	int max = bus->pktmax ;
	unsigned char * filedata;
	struct file_data_pkt * pkt;
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);

	pkt = (struct file_data_pkt *)&ASDU[IEC_INFO + bus->infoaddrlen];
	filedata = (unsigned char *)&pkt->data[0];

	remain  = tmp->fsize - tmp->ftell ;
	ftell   = tmp->ftell ;

	ASDU[IEC_TYPE]          = 0xD2                    ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_REQUEST             ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;
	ASDU[IEC_INFO]          = 0                       ;
	ASDU[IEC_INFO+1]        = 0                       ;
	ASDU[IEC_INFO+2]        = 0                       ;

	if (tmp->fsize == 0) {
		/* 如果文件大小为 0  */
		goto cleanup;
	}

	/* 计算当前包最大发送数据量，预留一个字节的和校验 */
	max = bus->pktmax - 1 - (filedata - ASDU);
	if (max > remain) {
		max = remain;
	}
	readn = iec_fread(tmp->iecfile,pkt->data,max);

cleanup:
	tmp->ftell += readn ;
	if (readn != max) {
		/* 读取错误 */
		readn = 0 ;
		ftell = 0 ;
		continuation = 0;
		bus->interrupt = NULL;
		iec104mst_fclose(bus,-1);
		iec10x_warn(bus,"read error!\r\n");
	}
	else 
	if (tmp->ftell == tmp->fsize) {
		continuation = 0;
	}
	else {
		continuation = 1;
	}

	pkt->type         = 2                    ;
	pkt->operate      = FILE_WR_DATA         ;
	pkt->ID[0]        = 0                    ;
	pkt->ID[1]        = 0                    ;
	pkt->ID[2]        = 0                    ;
	pkt->ID[3]        = 0                    ;
	pkt->ftell[0]     = (ftell >> 0 ) & 0xff ;
	pkt->ftell[1]     = (ftell >> 8 ) & 0xff ;
	pkt->ftell[2]     = (ftell >> 16) & 0xff ;
	pkt->ftell[3]     = (ftell >> 24) & 0xff ;
	pkt->continuation = continuation ;

	/* 计算和校验 */
	for (int i = 0 ; i < readn ; i++ , filedata++) {
		sumchk += *filedata;
	}
	*filedata++ = sumchk ;
	
	iec104_protocol_send(bus,ASDU,filedata - ASDU,0);
	iec10x_msg(bus,"file data bytes: %d\r\n",readn);
    #if (ACK_PACKET == ACK_SLIDING_WINDOW)
    #else
        bus->fs.todo = NULL;
    #endif
}


static void iec104mst_fwrite_ack(iec10x_t * bus, uint8_t * ASDU,int32_t len)
{
	/* 文件发送流程中间参数，存于 bus->fs.argv */
	struct frecv_arg * tmp = (struct frecv_arg *)bus->fs.argv;
	struct read_ack_pkt * pkt;
	int  ftell = 0 , result ;
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);

	pkt = (struct read_ack_pkt *)&ASDU[IEC_INFO + bus->infoaddrlen];

	result = pkt->continuation ;
	if (result) {
		/* 从站返回错误 */
		iec104mst_fclose(bus,-1);
		iec10x_warn(bus,"slave reply error:%d\r\n", result);
	}

	ftell |=  pkt->ftell[0]        ;
	ftell |= (pkt->ftell[1] << 8)  ;
	ftell |= (pkt->ftell[2] << 16) ;
	ftell |= (pkt->ftell[3] << 24) ;

	#if (ACK_PACKET == ACK_EACH_PACKET)
		if (ftell != tmp->ftell) {
			/* 从站返回错误 */
			iec10x_warn(bus,"slave ftell error:%d\r\n",ftell);
			iec104mst_fclose(bus,-1);
		}
		else
	#endif
	if (ftell == tmp->fsize) {
		/* 传输成功完成！ */
		if (tmp->callbackfn) {
			tmp->callbackfn(bus,tmp->file,0);
			tmp->callbackfn = NULL;
		}
		iec104mst_fclose(bus,0);
		iec10x_msg(bus,"send file successfully.\r\n");
	}
	else {
		bus->fs.todo = iec104mst_fwrite;
	}
}

/**
  * @author   古么宁
  * @brief    主站收到从机回复的文件激活确认帧
  * @param    bus      : 总线
  * @param    ASDUrx   : 接收到的 ASDU 数据包
  * @param    ASDUlen  : 接收到的 ASDU 数据包长度
  * @return   don't care
*/
static void iec104mst_fconfirm(iec10x_t * bus , uint8_t * ASDU,int len,int mode)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct frecv_arg * tmp ;
	struct file_confirm_pkt * pkt;
	unsigned char * size = 0;

	/* 附加数据包格式 @see struct file_confirm_pkt */
	pkt = (struct file_confirm_pkt *)&ASDU[IEC_INFO + bus->infoaddrlen];
	if (pkt->result > 0) {
		/* 从机回复文件激活失败 */
		iec104mst_fclose(bus,-1);
		return ;
	}

	/* 获取文件名 */
	pkt->name[pkt->namelen] = '\0';
	size = (unsigned char *)&pkt->name[pkt->namelen+4]; // 跳过 4 字节的文件 ID 得到文件大小
	tmp  = (struct frecv_arg *)bus->fs.argv;

	tmp->fsize  = size[0]       ;
	tmp->fsize |= size[1] << 8  ;
	tmp->fsize |= size[2] << 16 ;
	tmp->fsize |= size[3] << 24 ;

	tmp->iecfile = iec_fopen(bus->workdir,pkt->name,mode);
	if (tmp->iecfile == NULL) {
		/* 创建文件失败 */
		iec10x_warn(bus,"cannot open file %s\r\n",pkt->name);
		iec104mst_fclose(bus,-1);
		return ;
	}
	else {
		iec10x_msg(bus,"%s ready,size:%d\r\n",pkt->name,tmp->fsize);
	}

	if (mode == 'r') {
		/* 主站写命令 */
		bus->fs.todo = iec104mst_fwrite;
	}
}


/**
  * @author   古么宁
  * @brief    主站接收到从机的文件数据帧，进行确认回复
  * @param    bus      : 总线
  * @note     此函数交由 bus->fs.todo 调用
  * @return   don't care
*/
static void iec104mst_frecv_ack(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	unsigned char * end;
	unsigned char * ASDU = ASDU_TXBUFF(bus);
	struct frecv_arg * tmp = (struct frecv_arg *)bus->fs.argv;
	struct read_ack_pkt * pkt ;
	int continuation = (tmp->fsize == tmp->ftell) ? 0 : 1;

	bus->fs.todo = NULL;
	pkt = (struct read_ack_pkt *)&ASDU[IEC_INFO + bus->infoaddrlen];

	ASDU[IEC_TYPE]          = 0xD2                    ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_REQUEST             ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;
	ASDU[IEC_INFO]          = 0                       ;
	ASDU[IEC_INFO+1]        = 0                       ;
	ASDU[IEC_INFO+2]        = 0                       ;

	pkt->type     = 2                         ;
	pkt->option   = FILE_RD_DATA_ACK          ;
	pkt->ID[0]    = 0                         ;
	pkt->ID[1]    = 0                         ;
	pkt->ID[2]    = 0                         ;
	pkt->ID[3]    = 0                         ;
	pkt->ftell[0] = (tmp->ftell >> 0 ) & 0xff ;
	pkt->ftell[1] = (tmp->ftell >> 8 ) & 0xff ;
	pkt->ftell[2] = (tmp->ftell >> 16) & 0xff ;
	pkt->ftell[3] = (tmp->ftell >> 24) & 0xff ;

	end  = (unsigned char *)&pkt->continuation;
	*end++ = continuation          ;
	
	/* 回复确认帧 */
	iec10x_msg(bus,"mst file recv ack packet.\r\n");
	iec104_protocol_send(bus,ASDU,end-ASDU,0);
	bus->iec104.rxcheck = bus->iec104.rxnums ;

	if (!continuation) {
		/* 传输结束 */
		iec104mst_fclose(bus,0);
	}
}


/**
  * @author   古么宁
  * @brief    主站端接收从机文件数据包
  * @param    bus      : 总线
  * @param    ASDUrx   : 接收到的 ASDU 数据包
  * @param    ASDUlen  : 接收到的 ASDU 数据包长度
  * @return   don't care
*/
static void iec104mst_frecv(iec10x_t * bus , uint8_t * ASDU,int32_t ASDUlen)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	int ftell = 0, size;
	struct file_data_pkt * pkt;
	struct frecv_arg * tmp ;
	unsigned char * info , checksum = 0;

	/* 传输过程中间参数 */
	tmp = (struct frecv_arg *)bus->fs.argv;
	if (!tmp->iecfile) {
		iec10x_warn(bus,"file packet.\r\n");
		return ;
	}

	pkt = (struct file_data_pkt *)&ASDU[IEC_INFO + bus->infoaddrlen];
	ftell |= (pkt->ftell[0])      ;
	ftell |= (pkt->ftell[1] << 8) ;
	ftell |= (pkt->ftell[2] << 16);
	ftell |= (pkt->ftell[3] << 24);
	if (tmp->ftell != ftell) {
		iec10x_warn(bus,"Error ftell.\r\n");
		iec104mst_fclose(bus,-1);
		return ;
	}

	/* 计算当前包文件数据相关大小 */
	info = (unsigned char *)pkt->data ;
	size = ASDUlen - (info - ASDU) - 1 ;
	tmp->ftell += size;
	
	for (int i = 0 ; i < size ; i++) {
		checksum += info[i] ;
	}

	if (checksum != ASDU[ASDUlen-1]) {
		iec10x_warn(bus,"Error recv checksum.\r\n");
		iec104mst_fclose(bus,-1);
		return ;
	}

	if (size != iec_fwrite(tmp->iecfile,info,size)) {
		iec10x_warn(bus,"write file failed.\r\n");
		iec104mst_fclose(bus,-1);
		return ;
	}

	if (0 == pkt->continuation) {
		/* 无后续，即传输结束，回复确认帧 */
		if (tmp->ftell != tmp->fsize) {
			iec104mst_fclose(bus,-1);
		}
		//tmp->ftell = size + ftell ;
		bus->fs.todo = iec104mst_frecv_ack ;
		return ;
	}

	#if  (ACK_PACKET == ACK_SLIDING_WINDOW)

		/* 更新滑动窗口时发送读文件数据传输确认帧 */
		int recvcheck = (ASDU[-4] >> 1)|(ASDU[-3] << 7);
		if (recvcheck - bus->iec104.rxcheck >= bus->iec104.w - 1) {
			bus->fs.todo   = iec104mst_frecv_ack ;
		}

	#elif (ACK_PACKET == ACK_EACH_PACKET)
		/* 对每帧数据帧都回复数据确认帧 */
		bus->fs.todo   = iec104mst_frecv_ack ;
	#endif
}


/**
  * @author   古么宁
  * @brief    主机召唤从机的某个目录
  * @param    bus        : 总线
  * @param    dirname    : 需要召唤的从机目录名
  * @param    tfilter    : 为 0 时搜索所有文件，否则搜索时间段文件
  * @param    start      : tfilter 为 1 时需输入起始查询时间
  * @param    end        : tfilter 为 1 时需输入终止查询时间
  * @param    callbackfn : 召唤文件目录结束后的回调
  * @return   成功返回 0
*/
int iec10xmst_readdir(
	iec10x_t *        bus          ,
	const char *      dirname      ,
	int               tfilter      ,
	CP56Time2a_t *    start        ,
	CP56Time2a_t *    end          ,
	dir_callbackfn_t  callbackfn )
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct dir_cmd_arg arg = {0};
	struct dir_get * dirpkt ;
	int argsize = 0;
	unsigned char * ASDU = arg.ASDU;
	char          * info ;

	if (!dirname || !bus->connected || !(bus->mode & IEC_MASTER)) {
		return -1;
	}

	if (tfilter) {
		extern int time_cmp(struct CP56Time2a *, struct CP56Time2a *);
		if (!start || !end || (time_cmp(start, end) < 0)) {
			return -1;
		}
	}

	info = (char*)&ASDU[IEC_INFO + bus->infoaddrlen];

	ASDU[IEC_TYPE]          = 0xD2                    ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_REQUEST             ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	/* 填充目录召唤命令信息内容，操作标识为 DIR_READ */
	dirpkt = (struct dir_get *)info;
	dirpkt->type     = 2               ;
	dirpkt->operate  = DIR_READ        ;
	dirpkt->dirID[0] = 0               ;
	dirpkt->dirID[1] = 0               ;
	dirpkt->dirID[2] = 0               ;
	dirpkt->dirID[3] = 0               ;
	dirpkt->dirlen   = strlen(dirname) ;
	
	info = dirpkt->dirname  ;
	for (int i = 0 ; i < dirpkt->dirlen ; i++) {
		*info++ = *dirname++;
	}
	
	/* 加载时间过滤信息 */
	*info++ = tfilter ;
	if (tfilter) {
		if (NULL == start || NULL == end) {
			return -1;
		}
		memcpy(info,start,sizeof(struct CP56Time2a));
		info += sizeof(struct CP56Time2a);
		memcpy(info,end,sizeof(struct CP56Time2a));
		info += sizeof(struct CP56Time2a);
	}
	else {
		info += sizeof(struct CP56Time2a)*2;
	}

	arg.ASDUlen    = info - (char *)ASDU ;
	arg.callbackfn = callbackfn ;
	argsize = arg.ASDUlen + 1 + sizeof(dir_callbackfn_t) ;
	iec10xmst_event(bus,iec104mst_dir_command,&arg,argsize);

	return 0;
}



/**
  * @author   古么宁
  * @brief    主机召唤从机的某个文件
  * @param    bus        : 总线
  * @param    savedir    : 接收文件需要保存的本地文件夹
  * @param    file       : 读取从机的文件名
  * @param    callbackfn : 接收文件结束后的回调
  * @note
  * 读文件传输大致过程如下
  * <pre>
  *  master                           slave
  *    | ----TI=210,COT=6,operate=3---> | 主站读文件激活
  *    | <---TI=210,COT=7,operate=4---- | 从站读文件激活确认
  *    | <---TI=210,COT=5,operate=5---- | 从站读文件数据传输
  *    | <---         ...          ---- | 从站读文件数据传输
  *    | ----TI=210,COT=5,operate=6---> | 主站读文件数据确认
  * </pre>
  * 调用此函数后将在 iec104mst_fopen() 实现文件激活报文发送，
  * 从站返回激活确认后将在 iec104mst_fconfirm() 函数响应，然
  * 后从站开始发送文件数据包，由 iec104mst_frecv() 进行接收处理。
  * 在传输过程中如果出现异常，将执行 iec104mst_fexit() 进行处理。
  * @return   成功返回 0
*/
int iec10xmst_fget(
	iec10x_t *      bus          ,
	const char *    savedir      ,
	const char *    file         ,
	fs_callbackfn_t callbackfn )
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct read_cmd_pkt * pkt ;
	struct file_cmd_arg arg = {0};
	int argsize = 0;
	unsigned char * ASDU = arg.ASDU;
	char          * info ;

	if (!savedir || !file || !bus->connected) {
		return -1;
	}

	if (!(bus->mode & IEC_MASTER)) {
		iec10x_warn(bus,"master file servive api.\r\n");
		return -1;
	}

	info = (char*)&ASDU[IEC_INFO + bus->infoaddrlen];

	ASDU[IEC_TYPE]          = 0xD2                    ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_REQUEST             ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	/* 填充读文件激活命令信息内容，操作标识为 FILE_RD_CMD */
	pkt = (struct read_cmd_pkt*)info;
	pkt->type    = 2           ;
	pkt->option  = FILE_RD_CMD ;
	pkt->namelen = strlen(file);
	info = pkt->name;
	for (int i = 0 ; i < pkt->namelen ; i++){
		*info++ = file[i];
	}

	arg.ASDUlen    = info - (char *)arg.ASDU;
	arg.callbackfn = callbackfn;

	argsize = arg.ASDUlen + 1 + sizeof(callbackfn) ;
	iec10x_change_dir(bus,savedir);
	iec10xmst_event(bus,iec104mst_fopen,&arg,argsize);
	return 0;
}



/**
  * @author   古么宁
  * @brief    主机下发文件至从站
  * @param    bus        : 总线
  * @param    dir        : 需要发送的文件所在的文件夹
  * @param    file       : 需要发送的文件名
  * @param    callbackfn : 发送文件结束后的回调
  * @note
  * 发送文件传输大致过程如下
  * <pre>
  *  master                           slave
  *    | ----TI=210,COT=6,operate=7---> | 主站写文件激活
  *    | <---TI=210,COT=7,operate=8---- | 从站写文件激活确认
  *    | ----TI=210,COT=5,operate=9---> | 主站写文件数据传输
  *    | ----         ...          ---> | 主站写文件数据传输
  *    | <---TI=210,COT=5,operate=10--- | 从站写文件激活确认
  * </pre>
  * 调用此函数后将在 iec104mst_fopen() 实现主站读文件激活，
  * 从站返回激活确认，将在 iec104mst_fconfirm() 函数响应，然
  * 后从站端开始发送文件数据包，由 iec104mst_frecv() 进行接收处理。
  * 在传输过程中如果出现异常，将执行 iec104mst_fexit() 
  * 进行异常处理。
  * @return   成功返回 0
*/
int iec10xmst_fput(
	iec10x_t *      bus          ,
	const char *    dir          ,
	const char *    file         ,
	fs_callbackfn_t callbackfn )
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	struct file_cmd_arg arg = {0};
	int argsize = 0;
	unsigned char * ASDU = arg.ASDU;
	unsigned char * info ;
	int namelen , fsize;

	if (!dir || !file || !bus->connected) {
		return -1;
	}

	if (!(bus->mode & IEC_MASTER)) {
		iec10x_warn(bus,"master file servive api.\r\n");
		return -1;
	}

	namelen = strlen(dir);
	if (namelen > sizeof(bus->workdir)) {
		return -1;
	}

	namelen = strlen(file);
	if (namelen > 200) {
		return -1;
	}

	iec10x_change_dir(bus,dir);
	fsize = iec_fsize(bus->workdir,file);
	if (fsize < 0) {
		return -1;
	}

	ASDU[IEC_TYPE]          = 0xD2                    ;
	ASDU[IEC_QUALIFIER]     = 0x01                    ; 
	ASDU[IEC_COT]           = COT_REQUEST             ;
	ASDU[IEC_COT+1]         = bus->connectaddr & 0xff ;
	ASDU[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	ASDU[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	info = &ASDU[IEC_INFO + bus->infoaddrlen];
	*info++ = 2           ;
	*info++ = FILE_WR_CMD ;
	*info++ = namelen     ;
	for (int i = 0 ; i < namelen ; i++) {
		*info++ = file[i];
	}

	/* 填充文件 ID */
	for (int i = 0 ; i < 4 ; i++) {
		*info++ = 0;
	}

	/* 填充文件大小 */
	for (int i = 0 ; i < 4 ; i++) {
		*info++ = (fsize >> (i * 8)) & 0xff ;
	}

	arg.callbackfn = callbackfn;
	arg.ASDUlen    = info - ASDU;
	argsize        = arg.ASDUlen + 1 + sizeof(callbackfn) ;

	iec10xmst_event(bus,iec104mst_fopen,&arg,argsize);
	return 0;
}


/**
  * @author   古么宁
  * @brief    主机文件服务响应
  * @param    bus      : 总线
  * @param    ASDUrx   : 接收到的 ASDU 数据包
  * @param    ASDUlen  : 接收到的 ASDU 数据包长度
  * @return   don't care
*/
void iec104mst_file_service(iec10x_t * bus , uint8_t * ASDU,int32_t len)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t * info ;
	info = &ASDU[IEC_INFO + bus->infoaddrlen];
	if (info[0] != 2) {
		return ;
	}

	if (info[1] == DIR_READ_ACK) {
		/* 读目录命令激活，从站返回目录列表 */
		iec104mst_dir_confirm(bus,ASDU,len);
	}
	else 
	if (info[1] == FILE_RD_ACK) {
		/* 读文件激活命令确认，从站准备发送文件数据 */
		iec104mst_fconfirm(bus,ASDU,len,'w');
	}
	else
	if (info[1] == FILE_RD_DATA) {
		/* 读文件数据传输，接收来自从机的文件数据 */
		iec104mst_frecv(bus,ASDU,len);
	}
	else
	if (info[1] == FILE_WR_ACK) {
		/* 写文件激活命令确认，从机准备好接收数据 */
		iec104mst_fconfirm(bus,ASDU,len,'r');
	}
	else
	if (info[1] == FILE_WR_DATA_ACK) {
		/* 写文件数据传输确认，从机接收到数据包确认 */
		iec104mst_fwrite_ack(bus,ASDU,len);
	}
	else {
		iec10x_warn(bus,"Unknown file operation type[0x%x].\r\n",info[1]);
	}
}


#endif
